home *** CD-ROM | disk | FTP | other *** search
- ================
- = VGA Tutorial =
- = By =
- = Barny Mercer =
- ================
- Part I - PIXELS
- ----------------
-
- Firstly, *many* thanks to Richard Griffiths for porting the code in this
- tutorial to Pascal.
-
- Hi and welcome to the first issue of my (hopefully) weekly tutorial on
- programming graphics for the PC.
-
- This tutorial deals with writing pixels to the screen and looks at a couple
- of different methods of doing so.
-
- Before we start :
-
- -----------------------------------------------------------------------------
- DISCLAIMER
-
- The code for this tutorial, the compiled .EXEs and any ascociated text
- are freely distributable on the conditions that the contents of the
- original .ZIP file are distributed as one and that no changes are made
- to any of the data or information contained within.
- The author expresses no warranty implied or otherwise as to the
- suitabilty of this software for execution on any machine other than
- the system on which it was developed (although there should be no
- problems).
- The author also takes no responsibilty for damage resulting from the
- use of information, code or otherwise, obtained from this tutorial
- (again, I'd be surprised if such a thing did happen).
- Finally you should _NOT_ have paid anything for this tutorial.
- If you parted with any money (other than a reasonalbe price for a
- disk) then complain and get your money back. Please let me know too.
- This tutorial is FREE. Please do not abuse this.
- -----------------------------------------------------------------------------
-
- Sorry about all that, but unfortunately we live in a world where such things
- are a necessity.
-
- Right!
-
- There are a number video modes available on the VGA graphics card. Each one
- is identified by a value which is passes to the BIOS interrupt 10h
- required to call it. If this is nonsense to you then don't worry.
- All will become clear...... (I hope)
-
- During this tutorial we will be concentrating on one particular video mode.
-
- Mode 13h
- --------
-
- Mode 13h is 320 pixels wide by 200 pixels and capable of displaying 256
- colours on screen at one time. We therefore have 64000 pixels to play with.
-
- Each pixel on screen is represented by 1 byte of video memory. Graphics are
- basically created by setting the value of these bytes. A byte can store
- values in the range of 0 to 255, that's 256 different values available.
- Each one of these values is linked to a colour index which informs the VGA
- card which colour is wanted on screen.
-
- Easy? Absolutely.
-
- --------------------------
- So how do I place a pixel?
-
- In order to demonstrate we'll write a short program to select the appropriate
- video mode, place some pixels and return to text mode.
-
- That specification is already nicely broken into simple pieces so we will
- expand on each one.
-
- Select mode 13h
- ---------------
-
- The easiest and quickest way to do this is with assembly, but I will
- explain the principle involved first.
-
- The BIOS interrupt number for changing the VGA mode is 10h (that's
- 16 decimal, for anyone who's unfamiliar with hexadecimal).
- There are lots of other interrupt numbers for dealing with the VGA
- but we will concentrate on 10h for now.
-
- To switch to mode 13h we need to tell the ROM BIOS two things :
-
- 1. Which mode we want (13h in this case)
- 2. And what to do with that number (A video operation)
-
- So the code looks like this :
-
- // -----------------------------------
- // Mode 13h Selection - Barny Mercer
- // -----------------------------------
-
- void main(void)
- {
- _asm // tell the compiler that we are writing assembly code
- {
- mov ax, 13h // store 13h in AX
- int 10h // call interrupt
- }
- }
-
- Note the use of the AX register. The INT instruction passes the value
- stored in AX to the VGA card and this is what sets the video mode.
-
- By placing different values in AX we can access different video modes.
-
- If you are not familliar with assembly, then do not worry. You can
- still use the code, and learn as you go along. If you need a quick
- assembly tutorial then drop me a line.
-
- That's it! Erm... well not quite. This program will switch to
- mode 13h but leave you there when it termiates. We need a way of
- returning to text mode when we are done. Easy...!
-
- To return to text mode we just replace 13h in the above code with
- the appropriate video mode. 25 line text mode has the value 03h, so..
-
- // -----------------------------------
- // Text Mode Selection - Barny Mercer
- // -----------------------------------
-
- void main(void)
- {
- _asm
- {
- mov ax, 03h // store 03h in AX
- int 10h // call interrupt
- }
- }
-
- There... 2 programs for switching video modes.
-
- 'Errmmm...', I hear you say, 'They're not really very useful are
- they?!'. Well, no, but they illustrate what we need to know.
-
- To make them more useful we will turn them into a single function
- which can be called whenever you need to switch video modes.
-
- Take a look at this code:
-
- // ----------------------------------------
- // Mode Selection Function - Barny Mercer
- // ----------------------------------------
-
- // define preprocessor constants
- #define VID_320x200 0x0013 // 0x informs the compiler
- #define VID_TEXT 0x0003 // that the value is in hex
-
- void SetVideo( int Mode )
- {
- _asm
- {
- mov ax, [Mode] // store parameter in AX
- int 10h // call interrupt
- }
- }
-
- Now when you want to switch video modes in your programs you do so
- by calling the function as follows:
-
- SetVideo( VID_320x200 ); // select 320x200x256 - 13h
- or
- SetVideo( VID_TEXT ); // select text mode
-
- Easy peasy! (cheesy expression number 1.)
-
- Well... we've managed to cover 2 of the three steps that we outlined above, so
- without further ado, let's move on to the most interesting bit.
-
- Placing Pixels
- --------------
-
- There are essentially two ways that this can be done.
-
- Interrupts
-
- By using a smilar technique to the one above we can
- send a pixel to video memory via the ROM BIOS.
- Using BIOS however is a very slow way of doing things.
-
- Direct Memory Access (DMA)
-
- A faster method is to avoid the BIOS completely and
- write our value directly to memory.
-
- I'll talk first about the DMA method.
-
- In order to place a pixel we need to know two things. The location
- of the pixel and the colour. Obvious? Of course it is.
-
- Now comes the tricky bit. When you look at your monitor what do you
- see? 200 rows of 320 pixels. Fair enough, but that is not the way
- in which the screen is represented as data.
-
- Video memory is, for most purposes, treated just like ordinary memory.
- A specific point in memory is loacted by two values. Both of these
- are 16 bit values (range 0 - 65535). The first (SEGMENT) points to
- a 64k chunk of memory and the second (OFFSET) points to a specific
- byte inside that chunk. Video memory for the VGA has the segment
- 0xA000. Therefore we say that the address of the first byte of video
- memory is A000:0000 with the next byte appearing at (logically)
- A000:0001. Remember that these addresses are in hexadecimal.
-
- (A hexadecimal/binary/decemal explanation is not provided here, but
- if you need one then feel free to contact me and I'll be happy to
- oblige)
-
- The video memory offset therefore extends from 0000 (0) to
- FFFF (65535) (although only the first FA00 (64000 = 320x200) are
- visible.
-
- To place a pixel on screen, we stuff the appropriate value in the
- appropriate memory location! Hoorah!! <wave banners> <sing songs>
- <generally celebrate> <gradually realise that there must be a catch>
- <stop singing> <wait for bad news>
-
- While your image occurs on a 320x200 grid using a coordinate system,
- we are actually placing values in memory by specifying only 1 value.
- By modifying the offset we can point to a specific byte.
- Actually, it's not so difficult. A very simple formula allows us to
- locate the correct offset based on our (X, Y) values.
-
- offset = (Y * 320) + x
-
- Therefore, a point (55,100) has the offset
- ( ( 100 * 320 ) + 55 ) = 32055 (0x7D37)
-
- To place a white pixel at point (55,100) we would place the value
- 0x0F (15 dec) at byte 0x7D37. The address looks like this A000:7D37
-
- All okay so far? Good. I'm glad to hear it. What?! It's not?
- Ok, e-mail me for more details on memory too then.
-
- Ok. Now, we know how to find the correct memory location. How do we
- actually place a value there? I'm so glad you asked that question,
- because it shows that you are paying attention. So, moving steadily
- onward........
-
- PLACING A PIXEL!
- ----------------
-
- As I mentioned above there are basically two ways to do this. I will
- code the first and explain it as I go along. It is simpler because
- we don't have to calculate the offset or fiddle with memory.
-
- Here we go.....
-
- // -------------------------------------
- // PutIntPixel Function - Barny Mercer
- // -------------------------------------
-
- void PutIntPixel( int X, int Y, int Colour )
- {
- _asm
- {
- mov ah, 0x0C ; specify apropriate function
- mov al, [Colour] ; put required colour in low byte
- mov cx, [X] ; X coordinate
- mov dx, [Y] ; Y coordinate
- mov bx, 0x01
- int 10h ; execute interrupt
- }
- }
-
- I've demonstrated this function in Intpixel.exe (source also included)
-
- The biggest drawback to this function is that it is soooo _s_l_o_w_
- So now I will demonstrate the DMA principles that we examined earlier.
-
- Oh..! One extra thing before I begin coding.
- In order to access a specific memory segment we need to be able to
- point to it in some way. The way we do this is by using a pointer
- to the correct segment. Like this....
-
- unsigned char *vga = (unsigned char *)0xA0000000;
-
- Now the variable 'vga' can be used to reference our video memory.
-
- // -------------------------------------
- // PutDMAPixel Function - Barny Mercer
- // -------------------------------------
-
- void PutDMAPixel( int X, int Y, int Colour )
- {
- unsigned int Offset;
-
- Offset = (Y*320)+X; // this could be done
- on-the-fly but I have used
- a variable for clarity
-
- // memset(vga+(Y*320)+X, Colour, 1); // on-the-fly version
-
- memset(vga+Offset, Colour, 1); // note that you could place
- many pixels by replacing
- the 1
- }
-
- Ok. This is demonstrated in 'dmapixel.exe' (source also included).
-
- It's still not very fast though.
- 'What can we do to make it faster?' I hear you murmur through your
- boredom induced haze, uncertain as to weather asking the question was
- wise.
-
- I am afraid that in order to speed things up, we're going to have to
- do a bit more assembly. All set? No? Tough.....
-
- First, I will code the above routine in assembler and then we can
- look at making it faster. Ha-wun, Ha-two, Ha-wun, two, three, four..
- Ahem...
-
- // -------------------------------------
- // PutASMPixel Function - Barny Mercer
- // -------------------------------------
-
- void PutASMPixel( int X, int Y, int Colour )
- {
- _asm
- {
- mov ax, 0xA000 ; point AX to video memory
- mov es, ax ; move segment pointer to ES
- (actual pointer)
-
- ; calculate offset
- mov ax, [Y] ; place Y value in AX
- mul ax, 320 ; AX=Y*320
- add ax, [X] ; add X coordinate to AX
- ; now AX = (Y*320)+X, as per previous
- ; example
-
- ; move offset to offset pointer
- mov di, ax
-
- mov es:[di], [Colour] ; move colour to memory
- }
- }
-
- Ok. Looks complicated, but it isn't that bad really. It is basically
- the same as the previous one but written in assembly so that we can
- see what needs doing to speed it up a bit.
-
- The first thing that I notice is the use of the MUL command.
- A MUL is _very_ _very_ slow and this is what is slowing this
- function down. Sooooo.... let's remove it!
-
- 'Please Sir. How do we remove it and still find the correct offset?'
- chirrups a small voice from the back of the class.
-
- If Y = 104 then Y*320 = 33280
-
- 104 * 256 = 26624
- 104 * 64 = 6656
- --- -----
- 320 33280
-
- 'What advantage is there to doing it in two stages?, you may well ask.
- And that I will endevour to make clear. Take a look....
-
- SHL register, [value]
-
- The assembly SHL (SHift logical Left) command rotates a 16 bit value
- stored in the register passed by the number of binary places in
- 'value'.
-
- Looking at binary patters we can see the following:
-
- 104 x 320 = 33280
-
- 01101000 * 0000000101000000 = 0110100000000000 (26624)
- + 0001101000000000 ( 6656)
- ----------------
- = 1000001000000000 = 33280
- ----------------
-
- So by doing SHL ax, 8 ; ax = Y * 256
- SHL bx, 6 ; bx = Y * 64
- ADD ax, bx ; ax = (Y*256)+(Y*64) = (Y*320)
-
- Instead of MUL ax, 320 ; ax = (Y*320)
- what have we gained?
-
- Take a look at the following table:
-
- Operation Clocks (486) 1 clock = (1000/CPU speed Mhz) nanoseconds
-
- ADD 1
- MOV 1
- SHL 3
- MUL 26!
-
- So as you can see, method 1 requires only 7 clocks to execute while
- the MUL method requires 26 clock ticks.
-
- And there you have it!
-
- Oh.... There is a way to shave another 2 clocks off that multiply
- routine. Instead of doing (SHL ax, 8), a (MOV ah, al) will achieve
- the same effect but 2 clocks faster.
-
- Here is the finished PutPixel function.
-
- void PutASMPixel( int X, int Y, unsigned char Colour )
- {
- _asm
- {
- mov ax, 0xA000 ; point AX to video memory
- mov es, ax ; move segment pointer to ES
- ; (actual pointer)
- mov bx, [Y]
- mov ax, bx ; register to register is faster by 1 clock
-
- mov ah, al ; ax=y*256 + y
- mov al, 0 ; ax=y*256
-
- shl bx, 6 ; bx=y*64
- add bx, ax ; bx=y*320
-
- add bx, [X] ; ax=(y*320)+x
- mov di, bx ; move video pointer to correct place
-
- mov al, [Colour]
- mov es:[di], al ; move colour to memory
- }
- }
-
- And there you go! A nice fast assembly PutPixel routine.
-
- A lot of work? Well yes, but only because of the need for speed in
- a pixel routine. A slow PutPixel will result in other routines which
- require it to be slowed too.
-
- It is possible to create a faster PutPixel, but I'll leave you with
- this one for now.
-
- I hope that this tutorial has been helpful to you. It is by no means perfect,
- but that doesn't make it worthless.
-
- If there is anything in this tutorial which you would like further details on,
- then please do not hesitate to contact me.
-
- If you produce anything nice, then please send it to me. I am always keen
- to see other people's work.
-
- Comments on this tutorial will be gratefully recieved. Was it any use to you?
- Too informal? Too complicated? Not clear enough? Too simple?
- What subjects would you like to see covered here?
-
- -------------------------------------------------------------------------------
-
- 'The true measure of a hero, is one who can lay down his life for others
- in the knowledge that no one will ever know......'
-
- (Source unknown)
- ------------------------------------------------------------------------------
-
- Barny Mercer - (original code & text file) 29/7/95 @ 12:22am
-
- email : barny.mercer@zetnet.co.uk
- WWW : http://zetnet.co.uk/users/bmercer/
- voice : 01595 692097 (UK)
-
- Richard Griffiths - Pascal conversion
- email : richard.griffiths@zetnet.co.uk
- WWW : http://zetnet.co.uk/users/rgriff/
-
-